﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NumSharp;
using Newtonsoft.Json;
using DataType;
using System.IO;
using System.Diagnostics;
using DataType.CustomEnumType;

namespace MapCalclator
{
    public class Map
    {
        private int maxRow;
        private int maxCol;
        private int elevGrade;
        private int width;  // 六角格的半径，默认200m
        private Hex[,] hexes;
        private List<List<List<Dictionary<int, float>>>> cost;
        /* 第一维：[0-车辆机动, 1-车辆行军, 2-步兵机动, 3-空中机动]
        [
            [
                [
                    { "能通行的邻域坐标 int " : "通行代价 float" }
                ]
            ]
        ]
        */
        public NDArray see;
        /* 五维数组，第一维(旧版)[0]：地对地、[1]：空对空、[2]：地对空，第一维新版有10种取值
        [
            [
                [
                    [
                        [
                            can_see: boolean
                        ]
                    ]
                ]
            ]
        ]
        */

        public int MaxRow { get => maxRow; }

        public int MaxCol { get => maxCol; }

        public int ElevGrade { get => elevGrade; }

        public int Width { get => width; }

        public Hex this[int row, int col]  // 取代原来的basic字段
        {
            get
            {
                //小于0或者大于最大值返回数据
                //if (row < 0 || row >= maxRow || col < 0 || col >= maxCol) return hexes[row, col];
                //else
                //{
                //    Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
                //    return null;  // 取六角格时，如果输入坐标超出地图范围，将得到null值
                //}
                if (IsValid(row, col)) return hexes[row, col];
                return null;
            }
            set
            {
                //if (row < 0 || row >= maxRow || col < 0 || col >= maxCol) hexes[row, col] = value;
                //else Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
                if (IsValid(row, col)) hexes[row, col] = value;
            }
        }


        /// <summary>
        /// 从路径path中导入地图文件，包括basic.json、cost.pickle和see.npz
        /// </summary>
        /// <param name="path"></param>
        public Map(string path)
        {
            // 从basic.json文件中导入数据
            using (StreamReader r = new StreamReader(path + @"\basic.json"))
            {
                string json = r.ReadToEnd();
                Dictionary<string, object> data_dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
                hexes = JsonConvert.DeserializeObject<Hex[,]>(data_dict["map_data"].ToString());
                if (data_dict.ContainsKey("ele_grade")) elevGrade = JsonConvert.DeserializeObject<int>(data_dict["ele_grade"].ToString());
                else elevGrade = 20;  // 默认20m的高差
                if (data_dict.ContainsKey("radius")) width = JsonConvert.DeserializeObject<int>(data_dict["width"].ToString());
                else width = 200;  // 默认200m的半径
                maxRow = hexes.GetLength(0);
                maxCol = hexes.GetLength(1);
            }

            // 从cost.json文件中导入数据
            using (StreamReader r = new StreamReader(path + @"\cost.json"))
            {
                string json = r.ReadToEnd();
                cost = JsonConvert.DeserializeObject<List<List<List<Dictionary<int, float>>>>>(json);
            }

            // 从see.npz文件中导入数据
            var data_test = np.Load_Npz<bool[,,,,]>(path + @"\see.npz");
            see = data_test["data.npy"];
        }

        /// <summary>
        /// 检查坐标pos是否在地图内
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        private bool IsValid(int pos)
        {
            int row = pos / 100;
            int col = pos % 100;
            //Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
            //return (row >= 0 && row < maxRow) && (col >= 0 && col < maxCol);
            if ((row >= 0 && row < maxRow) && (col >= 0 && col < maxCol)) return true;
            else
            {
                Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
                return false;
            }
        }

        private bool IsValid(int row, int col)
        {
            //Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
            //return (row >= 0 && row < maxRow) && (col >= 0 && col < maxCol);
            if ((row >= 0 && row < maxRow) && (col >= 0 && col < maxCol)) return true;
            else
            {
                Trace.TraceWarning($"输入坐标[{row}, {col}]超出地图范围");
                return false;
            }
        }

        public Hex[,] GetMapData()
        {
            return hexes;
        }

        public int[] GetNeighbors(int pos)
        {
            if (!IsValid(pos))
            {
                return null;
            }
            int row = pos / 100;
            int col = pos % 100;
            return hexes[row, col].neighbors;
        }
        public int[] GetNeighbors(int row, int col)
        {
            if (!IsValid(row, col))
            {
                return null;
            }
            return hexes[row, col].neighbors;
        }

        public Dictionary<int, float> GetHexCosts(int pos, MoveMode moveMode)
        {
            if (!IsValid(pos))
            {
                return null;
            }
            int row = pos / 100;
            int col = pos % 100;
            return cost[(int)moveMode][row][col];
        }

        public Dictionary<int, float> GetHexCosts(int row, int col, MoveMode moveMode)
        {
            if (!IsValid(row, col))
            {
                return null;
            }
            return cost[(int)moveMode][row][col];
        }

        public bool CanSee(int pos1, int pos2, int mode = 0)
        {
            if (!IsValid(pos1))
            {
                return false;  // 出错就不能通视
            }
            if (!IsValid(pos2))
            {
                return false;
            }
            if (mode < 0 || mode >= see.shape[0])
            {
                Trace.TraceWarning($"输入了不正确的通视模式: {mode}");
                return false;
            }
            int row1 = pos1 / 100;
            int col1 = pos1 % 100;
            int row2 = pos2 / 100;
            int col2 = pos2 % 100;
            return see[mode][row1, col1, row2, col2];
        }

        public bool CanSeeTerrain(OprType type, OprType target_type, int opr_cur_hex, int target_cur_hex, OprSubType target_sub_type, Dictionary<OprSubType, int> opr_observe_distance, MoveStateType target_move_state)
        {
            int mode = GetMode(type, target_type);
            if (mode < 0) return false;

            if (!CanSee(opr_cur_hex, target_cur_hex, mode)) return false;

            int dist = GetDistance(opr_cur_hex, target_cur_hex);
            // 坦克对坦克的视野最大是25格
            int observe_dist = opr_observe_distance[target_sub_type];

            int row = target_cur_hex / 100;
            int col = target_cur_hex % 100;
            int opr_row = opr_cur_hex / 100;
            int opr_col = opr_cur_hex % 100;

            // 优势地形，视距减半
            if (new List<TerrainType> { TerrainType.Forest, TerrainType.City }.Contains(hexes[row, col].cond))
            {
                observe_dist = observe_dist / 2;
            }

            // 高程优势+掩蔽，视距减半 飞机对车辆除外
            if ((hexes[opr_row, opr_col].elev <= hexes[row, col].elev) && target_move_state == MoveStateType.Shelt && (type != OprType.Airplane || target_type != OprType.Vehicle))
            {
                observe_dist = observe_dist / 2;
            }

            if (dist > observe_dist) return false;

            return true;
        }

        public int GetDistance(int pos1, int pos2)
        {
            if (!IsValid(pos1))
            {
                return -1;  // -1表示不正确的距离
            }
            if (!IsValid(pos2))
            {
                return -1;
            }
            // Convert positions to cube coordinates
            int row1 = pos1 / 100;
            int col1 = pos1 % 100;
            int q1 = col1 - (row1 - (row1 & 1)) / 2;
            int r1 = row1;
            int s1 = -q1 - r1;
            int row2 = pos2 / 100;
            int col2 = pos2 % 100;
            int q2 = col2 - (row2 - (row2 & 1)) / 2;
            int r2 = row2;
            int s2 = -q2 - r2;
            // Calculate Manhattan distance
            return Math.Abs(q1 - q2) + Math.Abs(r1 - r2) + Math.Abs(s1 - s2) / 2;
        }

        /// <summary>
        /// 使用A*算法计算从begin到end的路径
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public List<int> GenMoveRoute(int begin, int end, int mode)
        {
            SortedSet<Tuple<float, float, int>> frontier = new SortedSet<Tuple<float, float, int>>();
            Dictionary<int, float> cost_so_far = new Dictionary<int, float>();
            Dictionary<int, int> came_from = new Dictionary<int, int>();


            if (!IsValid(begin))
            {
                return null;
            }
            if (!IsValid(end))
            {
                return null;
            }
            if (mode < 0 || mode >= cost.Count)
            {
                Trace.TraceWarning($"输入了不正确的通视模式: {mode}");
                return null;
            }
            if (begin == end)
            {
                Trace.TraceWarning($"机动路线计算中输入的起始位置和终止位置相同");
                return null;
            }

            // BFS with priority queue to find one of the fastest move path from `begin` to `end` with given `mode`
            frontier.Add(new Tuple<float, float, int>(0, (float)new Random().NextDouble(), begin));
            cost_so_far[begin] = 0;
            came_from[begin] = -1;  // -1表示没有父节点

            while (frontier.Count > 0)
            {
                Tuple<float, float, int> current = frontier.First();
                frontier.Remove(current);

                //float cost = current.Item2;
                int cur = current.Item3;
                int row = cur / 100;
                int col = cur % 100;

                if (cur == end) break;

                foreach (KeyValuePair<int, float> neigh_n_cost in cost[mode][row][col])
                {
                    int neigh = neigh_n_cost.Key;
                    float edge_cost = neigh_n_cost.Value;
                    float neighCost = cost_so_far[cur] + edge_cost;

                    if (cost_so_far.ContainsKey(neigh) && neighCost < cost_so_far[neigh]) continue;
                    cost_so_far[neigh] = neighCost;
                    came_from[neigh] = cur;
                    int heuristic = this.GetDistance(neigh, end);
                    frontier.Add(new Tuple<float, float, int>(neighCost + heuristic, (float)new Random().NextDouble(), neigh));
                }
            }

            List<int> path = new List<int>();

            if (came_from.ContainsKey(end))
            {
                int cur = end;
                while (cur != begin)
                {
                    path.Append(cur);
                    cur = came_from[cur];
                }
                path.Reverse();
            }

            return path;
        }

        /// <summary>
        /// 该想定中只涉及坦克，因此直接return 0
        /// </summary>
        /// <param name="opr_type"></param>
        /// <param name="target_type"></param>
        /// <returns></returns>
        public int GetMode(OprType opr_type, OprType target_type)
        {
            if ((opr_type == OprType.Infantry || opr_type == OprType.Vehicle) && target_type == OprType.Infantry || target_type == OprType.Vehicle)
            {
                return 0;  // 地对地模式
            }
            else if ((opr_type == OprType.Infantry || opr_type == OprType.Vehicle) && target_type == OprType.Airplane)
            {
                return 2;  // 地对空模式
            }
            else if ((target_type == OprType.Infantry || target_type == OprType.Vehicle) && opr_type == OprType.Airplane)
            {
                return 2;  // 空对地模式
            }
            else if (opr_type == OprType.Airplane && target_type == OprType.Airplane)
            {
                return 1;  // 空对空模式
            }
            else
            {
                Trace.TraceWarning($"暂时不能计算此输入的同时模式");
                return -1;  // 无此模式
            }
        }
    }
}
